package com.dg.framework.servlet;

import com.dg.framework.annotation.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 * @Auther: 我在人间写Bug--郭凯航
 * @Date: 2021/11/2 16:10
 * @Description:
 */
public class DispatcherServlet extends HttpServlet {
	
	//存储读取的配置文件
	private Properties properties = new Properties();
	//存储扫包的所有bean的全限定类名
	private List<String> classNames = new ArrayList<String>();
	//ioc容器
	Map<String, Object> ioc = new HashMap<String, Object>();
	//存储方法和url的映射
	Map<String, Method> methodMapping = new HashMap<String, Method>();
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doPost(req, resp);
	}
	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		//6、根据url调用指定方法
		try {
			doDispatcher(req, resp);
		}catch (Exception e){
			e.printStackTrace();
			resp.getWriter().write(" 500 "+e.getMessage());
		}finally {
		    resp.getWriter().close();
		}
	}
	
	private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) {
		
		try {
			//获取url
			String requestURI = req.getRequestURI();
			if (!methodMapping.containsKey(requestURI)) {
				throw new NullPointerException("404 notfound");
			}
			Method method = methodMapping.get(requestURI);
			//定义map,key存名称,value存索引位置
			HashMap<String, Integer> paramsIndexMap = new HashMap<String, Integer>();
			Annotation[][] parameterAnnotations = method.getParameterAnnotations();
			for (int i = 0; i < parameterAnnotations.length; i++) {
				for (Annotation annotation : parameterAnnotations[i]) {
					if (annotation instanceof DGRequestParam) {
						//如果是带注解DGRequestParam的参数
						String paramName = ((DGRequestParam) annotation).value();
						if (!"".equals(paramName.trim())) {
							paramsIndexMap.put(paramName, i);
						}
					}
				}
			}
			//存储request和response
			Class<?>[] paramTypes = method.getParameterTypes();
			for (int i = 0; i < paramTypes.length; i++) {
				Class<?> type = paramTypes[i];
				if (type == HttpServletRequest.class || type == HttpServletResponse.class) {
					paramsIndexMap.put(type.getName(), i);
				}
			}
			Object[] args = new Object[paramTypes.length];
			//想要调用到指定方法有个问题,必须将参数和方法上的参数进行匹配
			Map<String, Object[]> parameterMap = req.getParameterMap();
			for (Map.Entry<String, Object[]> entry : parameterMap.entrySet()) {
				String key = entry.getKey();
				if (!paramsIndexMap.containsKey(key)) {
					continue;
				}
				Integer integer = paramsIndexMap.get(key);
				args[integer] = Arrays.toString(entry.getValue())
						.replaceAll("\\[|\\]", "")
						.replaceAll("\\s", "");
			}
			if (paramsIndexMap.containsKey(HttpServletRequest.class.getName())) {
				int index = paramsIndexMap.get(HttpServletRequest.class.getName());
				args[index] = req;
			}
			
			if (paramsIndexMap.containsKey(HttpServletResponse.class.getName())) {
				int index = paramsIndexMap.get(HttpServletResponse.class.getName());
				args[index] = resp;
			}
			//获取方法所在类的实例化对象
			Object o = ioc.get(toLowerFirstCase(method.getDeclaringClass().getSimpleName()));
			method.invoke(o, args);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e.getMessage());
		}
	}
	
	@Override
	public void init(ServletConfig config) throws ServletException {
		//1、读取配置文件,确定扫包范围
		doConfig(config.getInitParameter("contextConfigLocation"));
		//2、收集扫描到的bean
		doScann(properties.getProperty("scanPackage"));
		//3、初始化ioc容器存储扫描到的bean
		doIocInstance();
		//4、ioc容器中的bean进行依赖注入，建立依赖关系,暂时不考虑循环依赖
		doAnnotation();
		//5、对应方法和url地址进行映射,准备调用
		doMethodMapping();
	}
	
	private void doMethodMapping() {
		if (ioc.isEmpty()) {
			return;
		}
		try {
			
			for (Map.Entry<String, Object> entry : ioc.entrySet()) {
				Class<?> clazz = entry.getValue().getClass();
				if (!clazz.isAnnotationPresent(DGController.class)) {
					continue;
				}
				//获取baseurl
				DGRequestMapping annotation = clazz.getAnnotation(DGRequestMapping.class);
				String baseUrl = annotation.value();
				for (Method method : clazz.getMethods()) {
					if (!method.isAnnotationPresent(DGRequestMapping.class)) {
						continue;
					}
					String methodUrl = method.getAnnotation(DGRequestMapping.class).value();
					String url = (baseUrl + "/" + methodUrl).replaceAll("/+", "/");
					methodMapping.put(url, method);
					System.out.println("methodMapping " + url + ":" + method.getName());
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private void doAnnotation() {
		if (ioc.isEmpty()) {
			return;
		}
		try {
			
			for (Map.Entry<String, Object> entry : ioc.entrySet()) {
				Class<?> clazz = entry.getValue().getClass();
				for (Field field : clazz.getDeclaredFields()) {
					if (!field.isAnnotationPresent(DGAutowired.class)) {
						continue;
					}
					DGAutowired annotation = field.getAnnotation(DGAutowired.class);
					String beanName = annotation.value().trim();
					if ("".equals(beanName)) {
						beanName = field.getType().getName();
					}
					//强制访问
					field.setAccessible(true);
					field.set(entry.getValue(), ioc.get(beanName));
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private void doIocInstance() {
		if (classNames.isEmpty()) {
			return;
		}
		try {
			for (String className : classNames) {
				Class<?> clazz = Class.forName(className);
				if (clazz.isAnnotationPresent(DGController.class)) {
					//初始化controller,简单处理,key值为类名首字母小写
					ioc.put(toLowerFirstCase(clazz.getSimpleName()), clazz.newInstance());
				} else if (clazz.isAnnotationPresent(DGService.class)) {
					//service,这里不扫描接口,简版
					String beanName = toLowerFirstCase(clazz.getSimpleName());
					DGService service = clazz.getAnnotation(DGService.class);
					//如果有别名,优先使用别名,如果没有别名,则使用类名首字母小写
					if (!"".equals(service.value())) {
						beanName = service.value();
					}
					//判断ioc容器中是否已经有了已经有了抛出异常
					if (ioc.containsKey(beanName)) {
						throw new IllegalArgumentException("ioc has bean " + beanName + ",please use alias");
					}
					ioc.put(beanName, clazz.newInstance());
				} else {
					continue;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private String toLowerFirstCase(String simpleName) {
		char[] chars = simpleName.toCharArray();
		chars[0] += 32;
		return String.valueOf(chars);
	}
	
	private void doScann(String scanPackage) {
		
		try {
			//将文件包路径切换成文件夹路径
			URL url = this.getClass().getClassLoader().getResource(scanPackage.replaceAll("\\.", "/"));
			File file = new File(url.getFile());
			for (File listFile : file.listFiles()) {
				String fileName = listFile.getName();
				//如果是文件夹进行递归循环,直到获取到文件夹内的文件
				if (listFile.isDirectory()) {
					doScann(scanPackage + "." + fileName);
				} else {
					//将扫描到的全限定类名全部存储进集合
					if (!fileName.endsWith(".class")) {
						continue;
					}
					classNames.add(scanPackage + "." + fileName.replaceAll(".class", ""));
				}
			}
			System.out.println("扫描到的类:" + classNames.toString());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/*
	 * @Author 我在人间写Bug--郭凯航
	 * @Description 读取配置文件,确定扫包范围
	 * @Date 2021/11/2 16:19
	 * @Param [config]
	 * @return void
	 **/
	private void doConfig(String contextConfigLocation) {
		InputStream is = null;
		try {
			is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
			//contextConfigLocation = this.getClass().getClassLoader().getResourceAsStream();
			properties.load(is);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				is.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	
}
